﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MediaPortal.GUI.Library;
using MediaPortal.Music.Database;
using MediaPortal.Profile;
using SQLite.NET;

namespace MusicInfoHandler
{
  class WebLookup
  {

    #region variables

    private readonly SQLiteResultSet _artistResults;
    private readonly SQLiteResultSet _albumResults;
    private static bool _strippedPrefixes;
    private static string _artistPrefixes;
    private const string StrArtistSQL = "select strArtist from artist where lower(strArtist) not in (select lower(strArtist) from artistinfo)";
    private const string StrAlbumSQL = "select distinct lower(strAlbum), rtrim(ltrim(lower(strAlbumArtist), '| '), ' |') from tracks " +
                               "EXCEPT " +
                               "select lower(strAlbum), lower(strArtist) from albumInfo";

    #endregion

    #region ctor

    /// <summary>
    /// CTOR but will also kick off separate threads to scrape artist and album info
    /// </summary>
    public WebLookup()
    {

      using (Settings xmlreader = new MPSettings())
      {
        _strippedPrefixes = xmlreader.GetValueAsBool("musicfiles", "stripartistprefixes", false);
        _artistPrefixes = xmlreader.GetValueAsString("musicfiles", "artistprefixes", "The, Les, Die");
      }

      _albumResults = MusicDatabase.DirectExecute(StrAlbumSQL);
      if (_albumResults.Rows.Count == 0)
      {
        Log.Info("MusicInfoHandler: No albums Found in Database");
      }
      else
      {
        var threadAlbum = new Thread(ProcessAlbums) {Name = "MusicInfoHandler: Album"};
        threadAlbum.Start();
      }

      _artistResults = MusicDatabase.DirectExecute(StrArtistSQL);
      if (_artistResults.Rows.Count == 0)
      {
        Log.Info("MusicInfoHandler: No artists Found in Database");
      }
      else
      {
        var threadArtist = new Thread(ProcessArtists) {Name = "MusicInfoHandler: Artist"};
        threadArtist.Start();
      }
    }

    #endregion

    #region handle lookups

    /// <summary>
    /// taken from audioscrobbler plugin code to reverse where prefix has been swapped 
    /// eg. The Beatles => Beatles, The or Die Toten Hosen => Toten Hosen ,Die
    /// and will change back to the artist name
    /// </summary>
    /// <param name="aStrippedArtist">Value stored in database with prefix at the end</param>
    /// <returns>What should be actual string in tag</returns>
    private static string UndoArtistPrefix(string aStrippedArtist)
    {
      try
      {
        string[] allPrefixes = null;
        allPrefixes = _artistPrefixes.Split(',');
        if (allPrefixes.Length > 0)
        {
          for (int i = 0; i < allPrefixes.Length; i++)
          {
            string cpyPrefix = allPrefixes[i];
            if (!aStrippedArtist.ToLowerInvariant().EndsWith(cpyPrefix.ToLowerInvariant())) continue;
            // strip the separating "," as well
            int prefixPos = aStrippedArtist.IndexOf(',');
            if (prefixPos <= 0) continue;
            aStrippedArtist = aStrippedArtist.Remove(prefixPos);
            cpyPrefix = cpyPrefix.Trim(new char[] { ' ', ',' });
            aStrippedArtist = cpyPrefix + " " + aStrippedArtist;
            // abort here since artists should only have one prefix stripped
            return aStrippedArtist;
          }
        }
      }
      catch (Exception ex)
      {
        Log.Error("AudioscrobblerBase: An error occured undoing prefix strip for artist: {0} - {1}", aStrippedArtist,
                  ex.Message);
      }

      return aStrippedArtist;
    }

    /// <summary>
    /// This is the artist thread.
    /// Loops over all artists which do not already have artist info and attempts to get it
    /// </summary>
    private void ProcessArtists()
    {
      var scraper = new AllmusicSiteScraper();
      foreach (var strArtist in _artistResults.Rows.Select(row => row.fields[0]).Where(strArtist => !string.IsNullOrEmpty(strArtist.Trim())).Where(strArtist => strArtist.ToLower() != "unknown" && strArtist.ToLower() != "various" && strArtist.ToLower() != "various artists"))
      {
        var strCleanArtist = strArtist;
        Log.Debug("Processing Artist: {0}", strArtist);
        if (_strippedPrefixes)
        {
          strCleanArtist = UndoArtistPrefix(strArtist);
        }

        var artistInfo = new MusicArtistInfo();
        List<AllMusicArtistMatch> artists;
        if (scraper.GetArtists(strCleanArtist, out artists))
        {
          if (artists.Count == 1)
          {
            var selectedMatch = artists[0];
            string strHtml;
            if (scraper.GetArtistHtml(selectedMatch, out strHtml))
            {
              if (artistInfo.Parse(strHtml))
              {
                var ai = artistInfo.Get();
                // because we are cleansing artist name during search it might no longer reflect the original
                // one tagged against tracks.   Reset this
                ai.Artist = strArtist;
                MusicDatabase.Instance.AddArtistInfo(ai);
                Log.Debug("Added ArtistInfo for: {0}", strArtist);
              }
            }
          }
        }

        Thread.Sleep(250);
      }
      Log.Info("Lookups complete");
    }

    /// <summary>
    /// This is the album thread.
    /// Loops over all albums which do not already have album info and attempts to get it
    /// </summary>
    private void ProcessAlbums()
    {
      IList<string> missingAlbums = new List<string>();
      foreach (var row in _albumResults.Rows)
      {
        var albumInfo = new MusicAlbumInfo();
        var strAlbum = row.fields[0].Trim();
        var strAlbumArtist = row.fields[1].Trim();
        var strCleanAlbumArtist = strAlbumArtist;

        if (String.IsNullOrEmpty(strAlbum) || string.IsNullOrEmpty(strAlbumArtist))
        {
          continue;
        }
        if (strAlbum.ToLower() == "unknown" || strAlbumArtist.ToLower() == "unknown" ||
            strAlbumArtist.ToLower() == "various" || strAlbumArtist.ToLower() == "various artists")
        {
          continue;
        }

        if (_strippedPrefixes)
        {
          strCleanAlbumArtist = UndoArtistPrefix(strAlbumArtist);
        }

        Log.Debug("Processing Album: {0} - {1}", strAlbumArtist, strAlbum);

        var scraper = new AllmusicSiteScraper();
        List<AllMusicArtistMatch> artists;

        if (scraper.GetArtists(strCleanAlbumArtist, out artists))
        {
          if (artists.Count == 1)
          {
            var selectedMatch = artists[0];
            string strAlbumHtml;
            if (scraper.GetAlbumHtml(strAlbum, selectedMatch.ArtistUrl, out strAlbumHtml))
            {
              if (albumInfo.Parse(strAlbumHtml))
              {
                var ai = albumInfo.Get();
                // because we are cleansing artist name during search it might no longer reflect the original
                // one tagged against tracks.   Reset this
                ai.AlbumArtist = strAlbumArtist;
                ai.Album = strAlbum;
                MusicDatabase.Instance.AddAlbumInfo(ai);
                Log.Debug("Added AlbumInfo for: {0} - {1}",strAlbum, strAlbumArtist);
              }
            }
          }          
        }

        Thread.Sleep(250);
      }
      Log.Info("Lookups complete");
    }

    #endregion

  }
}
